# -*- coding: utf-8 -*-
"""
Contains classes and utilities for serving a site
generated from hyde.
"""
import threading
import urllib
import traceback
from datetime import datetime


from hyde._compat import (HTTPServer, iteritems, parse, PY3,
                          SimpleHTTPRequestHandler, unquote)
from hyde.generator import Generator

from fswrap import File, Folder

from commando.util import getLoggerWithNullHandler
logger = getLoggerWithNullHandler('hyde.server')


class HydeRequestHandler(SimpleHTTPRequestHandler):

    """
    Serves files by regenerating the resource (or)
    everything when a request is issued.
    """

    def do_GET(self):
        """
        Identify the requested path. If the query string
        contains `refresh`, regenerat the entire site.
        Otherwise, regenerate only the requested resource
        and serve.
        """
        self.server.request_time = datetime.now()
        logger.debug("Processing request: [%s]" % self.path)
        result = parse.urlparse(self.path)
        query = parse.parse_qs(result.query)
        if 'refresh' in query or result.query == 'refresh':
            self.server.regenerate()
            if 'refresh' in query:
                del query['refresh']
            parts = list(tuple(result))
            parts[4] = urllib.urlencode(query)
            parts = tuple(parts)
            new_url = parse.urlunparse(parts)
            logger.info('Redirecting... [%s]' % new_url)
            self.redirect(new_url)
        else:
            SimpleHTTPRequestHandler.do_GET(self)

    def translate_path(self, path):
        """
        Finds the absolute path of the requested file by
        referring to the `site` variable in the server.
        """
        site = self.server.site
        path = unquote(self.path)
        if not PY3:
            path = path.decode('utf-8')
        result = parse.urlparse(path)
        logger.debug(
            "Trying to load file based on request: [%s]" % result.path)
        path = result.path.lstrip('/')
        res = None
        if path.strip() == "" or File(path).kind.strip() == "":
            deployed = site.config.deploy_root_path.child(path)
            deployed = Folder.file_or_folder(deployed)
            if isinstance(deployed, Folder):
                node = site.content.node_from_relative_path(path)
                res = node.get_resource('index.html')
            elif hasattr(site.config, 'urlcleaner') and hasattr(
                    site.config.urlcleaner, 'strip_extensions'):
                for ext in site.config.urlcleaner.strip_extensions:
                    res = site.content.resource_from_relative_deploy_path(
                        path + '.' + ext)
                    if res:
                        break
                for ext in site.config.urlcleaner.strip_extensions:
                    new_path = site.config.deploy_root_path.child(
                        path + '.' + ext)
                    if File(new_path).exists:
                        return new_path
        else:
            res = site.content.resource_from_relative_deploy_path(path)

        if not res:
            logger.error("Cannot load file: [%s]" % path)
            return site.config.deploy_root_path.child(path)
        else:
            self.server.generate_resource(res)
        new_path = site.config.deploy_root_path.child(
            res.relative_deploy_path)
        return new_path

    def do_404(self):
        """
        Sends a 'not found' response.
        """
        site = self.server.site
        if self.path != site.config.not_found:
            self.redirect(site.config.not_found)
        else:
            res = site.content.resource_from_relative_deploy_path(
                site.config.not_found)

            message = "Requested resource not found"
            if not res:
                logger.error(
                    "Cannot find the 404 template [%s]."
                    % site.config.not_found)
            else:
                f404 = File(self.translate_path(site.config.not_found))
                if f404.exists:
                    message = f404.read_all()
            self.send_response(200, message)

    def redirect(self, path, temporary=True):
        """
        Sends a redirect header with the new location.
        """
        self.send_response(302 if temporary else 301)
        self.send_header('Location', path)
        self.end_headers()


class HydeWebServer(HTTPServer):

    """
    The hyde web server that regenerates the resource, node or site when
    a request is issued.
    """

    def __init__(self, site, address, port):
        self.site = site
        self.site.load()
        self.generator = Generator(self.site)
        self.request_time = datetime.strptime('1-1-1999', '%m-%d-%Y')
        self.regeneration_time = datetime.strptime('1-1-1998', '%m-%d-%Y')
        self.__is_shut_down = threading.Event()
        self.__shutdown_request = False
        self.map_extensions()
        HTTPServer.__init__(self, (address, port),
                            HydeRequestHandler)

    def map_extensions(self):
        """
        Maps extensions specified in the configuration.
        """
        try:
            extensions = self.site.config.server.extensions.to_dict()
        except AttributeError:
            extensions = {}

        for extension, type in iteritems(extensions):
            ext = "." + extension if not extension == 'default' else ''
            HydeRequestHandler.extensions_map[ext] = type

    def regenerate(self):
        """
        Regenerates the entire site.
        """
        try:
            logger.info('Regenerating the entire site')
            self.regeneration_time = datetime.now()
            if self.site.config.needs_refresh():
                self.site.config.reload()
            self.site.load()
            self.generator.generate_all(incremental=False)
        except Exception as exception:
            logger.error('Error occured when regenerating the site [%s]'
                         % exception.message)
            logger.debug(traceback.format_exc())

    def generate_node(self, node):
        """
        Generates the given node.
        """

        deploy = self.site.config.deploy_root_path
        if not deploy.exists:
            return self.regenerate()

        try:
            logger.debug('Serving node [%s]' % node)
            self.generator.generate_node(node, incremental=True)
        except Exception as exception:
            logger.error(
                'Error [%s] occured when generating the node [%s]'
                % (repr(exception), node))
            logger.debug(traceback.format_exc())

    def generate_resource(self, resource):
        """
        Regenerates the given resource.
        """
        deploy = self.site.config.deploy_root_path
        if not deploy.exists:
            return self.regenerate()
        dest = deploy.child_folder(resource.node.relative_path)
        if not dest.exists:
            return self.generate_node(resource.node)
        try:
            logger.debug('Serving resource [%s]' % resource)
            self.generator.generate_resource(resource, incremental=True)
        except Exception as exception:
            logger.error(
                'Error [%s] occured when serving the resource [%s]'
                % (repr(exception), resource))
            logger.debug(traceback.format_exc())
